Skip to content

feat(standards): add NetworkAccount auth component#2817

Open
partylikeits1983 wants to merge 11 commits intoajl-kernel-tx-script-rootfrom
ajl-network-account-component
Open

feat(standards): add NetworkAccount auth component#2817
partylikeits1983 wants to merge 11 commits intoajl-kernel-tx-script-rootfrom
ajl-network-account-component

Conversation

@partylikeits1983
Copy link
Copy Markdown
Contributor

@partylikeits1983 partylikeits1983 commented Apr 22, 2026

Resolves: #2814

Summary

Adds a new auth component intended for network-owned accounts (AggLayer bridge, AggLayer faucet). Replaces the NoAuth pattern which lets any transaction emit output notes authored by the account and thus forge bridge-authored MINT notes (see #2797).

The auth procedure enforces two invariants:

  1. Rejects the transaction if a tx script was executed, using tx::get_script_root from the kernel (added in feat(kernel): expose tx script root via public kernel procedure #2816).
  2. Rejects the transaction if any consumed input note's script root is not present in the component's whitelist.

If both checks pass, the nonce is incremented when the account state changed or the account is new, matching the NoAuth / SingleSig behavior.

Changes

  • asm/account_components/auth/network_account.masm — new auth procedure auth_tx_network_account.
  • src/account/auth/network_account.rsNetworkAccount Rust struct with From<NetworkAccount> for AccountComponent and unit tests.
  • src/account/components/mod.rs — registers NETWORK_ACCOUNT_LIBRARY, adds network_account_library(), extends StandardAccountComponent with AuthNetworkAccount.
  • src/account/interface/component.rs, extension.rs — extend AccountComponentInterface with AuthNetworkAccount (maps to AuthMethod::NoAuth for now; a distinct AuthMethod variant can be added later if needed).
  • tests/auth/network_account.rs — integration tests covering: tx-script rejection, non-whitelisted-note rejection, and the happy path.

Storage layout

  • Single map slot at miden::standards::auth::network_account::whitelist.
  • Keys: note script roots (Word). Values: sentinel [1, 0, 0, 0] marking presence.
  • Fixed at account creation (mutable whitelist is a possible future improvement).

Stacking

This PR is stacked on #2816 (PR 1 of the #2797 fix chain). It is the second PR of three; the AggLayer wire-up in #2815 will land on top.

Closes #2814. Related to #2797.

Adds a new auth component intended for network-owned accounts such as the
AggLayer bridge and the AggLayer faucet. Replaces the NoAuth pattern
which lets any transaction emit output notes authored by the account.

The auth procedure enforces two invariants:

1. Rejects the transaction if a tx script was executed, using
   `tx::get_script_root` from the kernel.
2. Rejects the transaction if any consumed input note's script root is
   not present in the whitelist stored at a well-known storage slot.

If both checks pass, the nonce is incremented when the account state
changed or the account is new, matching NoAuth/SingleSig behavior.

The whitelist is a StorageMap keyed by script root (with a sentinel
value marking presence) and is fixed at account creation. A mutable
whitelist is a possible future improvement.

- new network_account.masm auth component
- new NetworkAccount Rust struct with From<NetworkAccount> for
  AccountComponent
- register library in components::mod and extend StandardAccountComponent
- extend AccountComponentInterface and AccountInterfaceExt with
  AuthNetworkAccount variant (maps to AuthMethod::NoAuth for now)
- unit tests for the Rust builder
- integration tests in tests/auth/network_account.rs covering
  tx-script-reject, non-whitelisted-note-reject, and happy-path

Part of #2814. Depends on #2813 (merged via #2816).
Related to the #2797 fix.
@partylikeits1983 partylikeits1983 changed the title feat(standards): add NetworkAccount auth component feat(standards): add NetworkAccount auth component Apr 22, 2026
Drop the stale AggLayer-faucet reference in the doc comment (the
component is used by network faucets more generally) and rewrite the
WHITELIST_SENTINEL comment to explain why a non-empty marker value is
needed (storage maps use the empty word as the "key absent" marker).
Also shorten the fully qualified miden_protocol::Felt path to use a
direct Felt import, matching the pattern used elsewhere in the crate.
…work_account.masm

Document the post-stack state after the if.true/end block that
conditionally increments the nonce, matching the commenting style used
by the rest of the procedure.
…Account

Update the auth call site and drop the stale CHANGELOG bullet left
behind by the merge of `ajl-kernel-tx-script-root`.
… example accounts

Reword the doc-comment example from "AggLayer bridge and a network
faucet" to "network faucets and the AggLayer bridge" to foreground
the general case over the specific deployment.

Expand the WHITELIST_SENTINEL comment to define the term "sentinel
value" and explain why we call the constant one: we only check that
the stored value differs from the empty word, so the contents carry
no information on their own.
@partylikeits1983 partylikeits1983 self-assigned this Apr 23, 2026
@partylikeits1983 partylikeits1983 added standards Related to standard note scripts or account components agglayer PRs or issues related to AggLayer bridging integration pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority labels Apr 23, 2026
@partylikeits1983 partylikeits1983 marked this pull request as ready for review April 23, 2026 20:19
Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I took a high level look.

As mentioned in #2797 (comment), I would suggest renaming this from the more general NetworkAccount to something like NoteScriptAllowlistAuth.

Also, I suggest renaming whitelist to allowlist.

Comment on lines +19 to +28
/// Builds a minimal account that uses the [`NetworkAccount`] auth component with the provided
/// whitelist of input-note script roots.
fn build_network_account(allowed_script_roots: Vec<Word>) -> anyhow::Result<Account> {
Ok(AccountBuilder::new([0; 32])
.with_auth_component(NetworkAccount::new(allowed_script_roots))
.with_component(BasicWallet)
.account_type(AccountType::RegularAccountUpdatableCode)
.storage_mode(AccountStorageMode::Public)
.build_existing()?)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this seems a bit misleading because the function name suggest we build a "network account", but, at least currently, network accounts are defined as accounts with AccountStorageMode::Network, but this one is public.

Comment on lines +25 to +28
static WHITELIST_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
StorageSlotName::new("miden::standards::auth::network_account::whitelist")
.expect("storage slot name should be valid")
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd call this "allow list", which is a bit more consistent with "block list" (used in other contexts #2819).


/// Creates a new [`NetworkAccount`] component with the provided list of allowed input-note
/// script roots.
pub fn new(allowed_script_roots: Vec<Word>) -> Self {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me wish we had a NoteScriptRoot newtype wrapper like AccountProcedureRoot for more type-safety. Maybe we should create an issue to introduce this?

(
Self::whitelist_slot().clone(),
StorageSlotSchema::map(
"Allowed input-note script roots",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Allowed input-note script roots",
"Allowed input note script roots",

Comment on lines +37 to +42
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Invocation: call
@auth_script
pub proc auth_tx_network_account
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This takes auth args as inputs, right? If so, I'd explicitly drop them here for safety.

Comment on lines +44 to +48
exec.tx::get_tx_script_root
# => [TX_SCRIPT_ROOT, pad(16)]

padw exec.word::eq
# => [no_tx_script, pad(16)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
exec.tx::get_tx_script_root
# => [TX_SCRIPT_ROOT, pad(16)]
padw exec.word::eq
# => [no_tx_script, pad(16)]
exec.tx::get_tx_script_root
# => [TX_SCRIPT_ROOT, pad(16)]
exec.word::eqz
# => [has_no_tx_script, pad(16)]

Comment on lines +59 to +61
# => [i, pad(16)]
sub.1
# => [i-1, pad(16)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# => [i, pad(16)]
sub.1
# => [i-1, pad(16)]
# => [note_idx+1, pad(16)]
sub.1
# => [note_idx, pad(16)]

Nit: We could rename i to note_idx for more clarity.

exec.active_account::get_map_item
# => [VALUE, i-1, pad(16)]

padw exec.word::eq not
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
padw exec.word::eq not
exec.word::eqz not

Nit

@bobbinth
Copy link
Copy Markdown
Contributor

bobbinth commented Apr 26, 2026

As mentioned in #2797 (comment), I would suggest renaming this from the more general NetworkAccount to something like NoteScriptAllowlistAuth.

I don't mind generalizing this, but then I don't think NoteScriptAllowlistAuth would be an auth component as checking whether a note belongs to the allowed set of notes would be done as a part of another auth component (e.g., a component that also checks a signature). I think there are a couple of ways to go about this:

Approach 1

We could move most of the logic into a module somewhere in the standards library (e.g., under miden::standards::auth namespace). The logic could be split across two procedures - something like:

#! Inputs:  []
#! Outputs: []
pub proc assert_no_tx_script
    ...
end

#! Inputs:  [allowed_note_scripts_slot_id_prefix, allowed_note_scripts_slot_id_suffix]
#! Outputs: []
pub proc assert_all_input_notes_allowed
    ...
end

Then, these procedures could be used from the AuthNetworkAccount auth component and also from future components that would be used by non-network accounts to limit a set of notes that can be executed against them.

Approach 2

Put the logic of the into a separate component NoteScriptAllowlist (or something similar), and then also create an AuthNetworkAccount that would depend on this component.


Between these two approaches, I have a slight preference towards the first approach - mostly because I think it is a bit simpler and doesn't require us to think too much about how this functionality will be used outside of network components in the future.

Also, I suggest renaming whitelist to allowlist.

If we go with approach 1 above, I'd actually name the storage slot something like:

miden::standards::auth::network_account::allowed_note_scripts

@PhilippGackstatter
Copy link
Copy Markdown
Contributor

I don't mind generalizing this, but then I don't think NoteScriptAllowlistAuth would be an auth component as checking whether a note belongs to the allowed set of notes would be done as a part of another auth component (e.g., a component that also checks a signature). I think there are a couple of ways to go about this:

Do you mean that the logic in assert_no_tx_script and assert_all_input_notes_allowed is general enough to be used by multiple auth components?

If so, I agree on the assert_no_tx_script part - it only relies on the newly introduced kernel procedure but has no dependency on storage, so it is indeed very generic.

The assert_all_input_notes_allowed procedure requires a well-defined storage layout in an account's storage. So, I would support this with an AllowedNoteScripts type that is essentially a standardized storage slot, a bit like the (soon to be removed) TokenMetadata:

impl From<TokenMetadata> for StorageSlot {
fn from(metadata: TokenMetadata) -> Self {
StorageSlot::with_value(TokenMetadata::metadata_slot().clone(), metadata.into())
}
}

This type defines the storage layout conversion and the slot name (miden::standards::auth::network_account::allowed_note_scripts).

So, I agree with approach 1 and would scope this functionality under:

  • miden::standards::auth::network_account::assert_no_tx_script
  • miden::standards::auth::network_account::assert_all_input_notes_allowed

And support this with the AllowedNoteScripts type.

The only open question is how a network account would easily add this auth functionality in a "one-liner", and for this I think we need an auth component like NoteScriptAllowlistAuth.

@bobbinth
Copy link
Copy Markdown
Contributor

The assert_all_input_notes_allowed procedure requires a well-defined storage layout in an account's storage.

I was thinking that this procedure would take the storage slot name as a parameter (see the signature in my comment). This way, different auth components could have their own storage slots to hold the set of allowed notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agglayer PRs or issues related to AggLayer bridging integration pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority standards Related to standard note scripts or account components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants